package utils

import (
	"errors"
	"fmt"
	"math/rand"
	"regexp"
	"strings"
	"time"

	"{{cookiecutter.app_name}}/common/cronexpr"

	"github.com/jinzhu/copier"
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

func findMinAndMax(a []time.Duration) (min time.Duration, max time.Duration) {
	min = a[0]
	max = a[0]
	for _, value := range a {
		if value < min {
			min = value
		}
		if value > max {
			max = value
		}
	}
	return min, max
}

func PrevCronTime(cronline string, t time.Time) time.Time {
	expr := cronexpr.MustParse(cronline)
	next1 := expr.Next(t)
	prevTime := expr.Next(t)

	m := make(map[time.Duration]int)

	for i := 0; i < 10; i++ {
		nextTime := expr.Next(prevTime)
		duration := nextTime.Sub(prevTime)
		prevTime = nextTime

		if _, ok := m[duration]; !ok {
			m[duration] = i
		}
	}

	keys := make([]time.Duration, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}

	_, b := findMinAndMax(keys)
	x0 := t.Add(-b)

	prev0 := expr.Next(x0)

	for i := 0; i < 10; i++ {
		if expr.Next(prev0) == next1 {
			return prev0
		}
		prev0 = expr.Next(prev0)
	}

	return time.Time{}
}

func NextCronTime(cronline string, t time.Time) time.Time {
	expr := cronexpr.MustParse(cronline)
	nextTime := expr.Next(t)
	return nextTime
}

func MaxInt64(x, y int64) int64 {
	if x < y {
		return y
	}
	return x
}

func MaxInt(x, y int64) int64 {
	if x < y {
		return y
	}
	return x
}

// Min returns the smaller of x or y.
func MinInt64(x, y int64) int64 {
	if x > y {
		return y
	}
	return y
}

func MinInt(x, y int) int {
	if x > y {
		return y
	}
	return y
}

func AbsInt(x int) int {
	if x < 0 {
		return -x
	}
	return x
}

func AbsInt64(x int64) int64 {
	if x < 0 {
		return -x
	}
	return x
}

func RandomNumber(min int, max int) int {
	return rand.Intn(max-min+1) + min
}

// FirstUpper 字符串首字母大写
func FirstUpper(s string) string {
	if s == "" {
		return ""
	}
	return strings.ToUpper(s[:1]) + s[1:]
}

// FirstLower 字符串首字母小写
func FirstLower(s string) string {
	if s == "" {
		return ""
	}
	return strings.ToLower(s[:1]) + s[1:]
}

const (
	Int64 int64 = 0
)

func MyCopier(toValue interface{}, fromValue interface{}) {
	copier.CopyWithOption(toValue, fromValue, copier.Option{
		IgnoreEmpty: true,
		DeepCopy:    false,
		Converters: []copier.TypeConverter{
			{
				SrcType: time.Time{},
				DstType: copier.String,
				Fn: func(src interface{}) (interface{}, error) {
					s, ok := src.(time.Time)
					if !ok {
						return nil, errors.New("src type not matching")
					}
					return s.Format(time.RFC3339), nil
				},
			},
			{
				SrcType: Int64,
				DstType: copier.String,
				Fn: func(src interface{}) (interface{}, error) {
					return fmt.Sprintf("%v", src), nil
				},
			},
		},
	})
}

const (
	primeNum = 982451653 // 是一个质数
)

// 混淆函数
func Obfuscate(id uint64) uint64 {
	return id * uint64(primeNum)
}

// 假设我们已知 id 都在 uint64 一半以下，我们可以这么做
func Deobfuscate(id uint64) uint64 {
	return id / uint64(primeNum)
}

// 身份证格式检查函数
func IsIdCard(idCard string) bool {
	idRegex := `^(\d{17})([0-9]|X)$`
	r, _ := regexp.Compile(idRegex)
	return r.MatchString(strings.ToUpper(idCard))
}

// 是否超过18岁
func IsOver18(idCard string) (bool, error) {
	if len(idCard) != 18 {
		return false, fmt.Errorf("invalid ID card length")
	}

	year, err := time.Parse("2006", idCard[6:10])
	if err != nil {
		return false, err
	}

	month, err := time.Parse("01", idCard[10:12])
	if err != nil {
		return false, err
	}

	day, err := time.Parse("02", idCard[12:14])
	if err != nil {
		return false, err
	}

	birthDay := time.Date(year.Year(), month.Month(), day.Day(), 0, 0, 0, 0, time.Now().Location())
	today := time.Now()

	return today.Year()-birthDay.Year() >= 18, nil
}
